﻿

#include "gtest/gtest.h"
#include "log.h"

#include <folly/fibers/FiberManagerMap.h>


#include <array>
#include <atomic>
#include <thread>
#include <vector>

#include <folly/Conv.h>
#include <folly/Memory.h>
#include <folly/Random.h>
#include <folly/executors/CPUThreadPoolExecutor.h>
#include <folly/experimental/coro/BlockingWait.h>
#include <folly/fibers/AddTasks.h>
#include <folly/fibers/AtomicBatchDispatcher.h>
#include <folly/fibers/BatchDispatcher.h>
#include <folly/fibers/BatchSemaphore.h>
#include <folly/fibers/EventBaseLoopController.h>
#include <folly/fibers/ExecutorLoopController.h>
#include <folly/fibers/FiberManager.h>
#include <folly/fibers/FiberManagerMap.h>
#include <folly/fibers/GenericBaton.h>
#include <folly/fibers/Semaphore.h>
#include <folly/fibers/SimpleLoopController.h>
#include <folly/fibers/TimedMutex.h>
#include <folly/fibers/WhenN.h>
#include <folly/futures/Future.h>
#include <folly/io/async/ScopedEventBaseThread.h>
#include <folly/portability/GTest.h>
#include <folly/tracing/AsyncStack.h>
using namespace folly::fibers;

using folly::Try;

TEST(FiberManager, batonTimedWaitTimeout) {
    bool taskAdded = false;

    FiberManager manager(std::make_unique<SimpleLoopController>());
    auto& loopController =
            dynamic_cast<SimpleLoopController&>(manager.loopController());
    std::chrono::steady_clock::time_point start;

    auto loopFunc = [&]() {
        if (!taskAdded) {
            manager.addTask([&]() {
                Baton baton;

                start = std::chrono::steady_clock::now();
                constexpr auto kTimeout = std::chrono::milliseconds(230);
                auto res = baton.try_wait_for(kTimeout);
                auto elapsedMs = std::chrono::duration_cast<std::chrono::milliseconds>(
                        std::chrono::steady_clock::now() - start);

                EXPECT_FALSE(res);
                EXPECT_LE(kTimeout, elapsedMs);

                loopController.stop();
            });
            taskAdded = true;
        } else {
            std::this_thread::sleep_for(std::chrono::milliseconds(50));
        }
    };

    loopController.loop(std::move(loopFunc));
}

static void f(int *p) {
    while (true)
    {


        LOG(INFO) << "f()";
        // Make sure recursion is not optimized out
        int a[100];
        for (size_t i = 0; i < 100; ++i)
        {
            a[i] = i;
            ++(a[i]);
            if (p)
            {
                a[i] += p[i];
            }
        }
    }
}

TEST(fibers, StackOverflow) {
    folly::EventBase evb;
    folly::fibers::getFiberManager(evb).addTask([&]()
                                                { f(nullptr); });
    evb.loop();
}
